﻿#region Copyright(C) 2010-2010 NCS Pte. Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2010 NCS Pte. Ltd. All rights reserved.
//
// SYSTEM NAME	    : iConnect .Net Application Framework
// COMPONENT ID	    : NCS.IConnect.Core.Wcf
// COMPONENT DESC   : Core components for WCF.	
//
// CREATED DATE/BY  : 18 Aug 2010 / Gao Ye
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using NCS.IConnect.Core.Utilities;
using System;
using System.IO;
using System.IO.Compression;
using System.ServiceModel.Channels;

namespace NCS.IConnect.Core.Wcf.Compression
{
    /// <summary>
    /// The custom <see cref="MessageEncoder"/> use for compression.
    /// </summary>
    public class CompressedMessageEncoder : MessageEncoder
    {
        /// <summary>
        /// Gets the inner <see cref="MessageEncoder"/>.
        /// </summary>
        public MessageEncoder InnerEncoder { get; private set; }

        /// <summary>
        /// Gets the compressor.
        /// </summary>
        public Compressor Compressor { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CompressedMessageEncoder"/> class.
        /// </summary>
        /// <param name="innerEncoder">The inner encoder.</param>
        /// <param name="compressor">The compressor.</param>
        public CompressedMessageEncoder(MessageEncoder innerEncoder, Compressor compressor)
        {
            Guard.ArgumentNotNull(innerEncoder, "innerEncoder");
            Guard.ArgumentNotNull(compressor, "compressor");

            this.InnerEncoder = innerEncoder;
            this.Compressor = compressor;
        }

        /// <summary>
        /// When overridden in a derived class, gets the MIME content type used by the encoder.
        /// </summary>
        /// <returns>The content type that is supported by the message encoder.</returns>
        public override string ContentType
        {
            get
            {
                return this.Compressor.ContentType;
            }
        }

        /// <summary>
        /// When overridden in a derived class, gets the media type value that is used by the encoder.
        /// </summary>
        /// <returns>The media type that is supported by the message encoder.</returns>
        public override string MediaType
        {
            get { return this.Compressor.MediaType; }
        }

        /// <summary>
        /// When overridden in a derived class, gets the message version value that is used by the encoder.
        /// </summary>
        /// <returns>The <see cref="T:System.ServiceModel.Channels.MessageVersion"/> that is used by the encoder.</returns>
        public override MessageVersion MessageVersion
        {
            get { return InnerEncoder.MessageVersion; }
        }
                
        /// <summary>
        /// When overridden in a derived class, reads a message from a specified stream.
        /// </summary>
        /// <param name="buffer">A <see cref="T:System.ArraySegment`1"/> of type <see cref="T:System.Byte"/> that provides the buffer from which the message is deserialized.</param>
        /// <param name="bufferManager">The <see cref="T:System.ServiceModel.Channels.BufferManager"/> that manages the buffer from which the message is deserialized.</param>
        /// <param name="contentType">The Multipurpose Internet Mail Extensions (MIME) message-level content-type.</param>
        /// <returns>
        /// The <see cref="T:System.ServiceModel.Channels.Message"/> that is read from the stream specified.
        /// </returns>
        public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
        {
            Guard.ArgumentNotNull(buffer, "buffer");
            Guard.ArgumentNotNull(bufferManager, "bufferManager");

            ArraySegment<byte> decompressedBuffer = this.Compressor.Decompress(buffer, bufferManager);
            Message returnMessage = InnerEncoder.ReadMessage(decompressedBuffer, bufferManager);
            returnMessage.Properties.Encoder = this;
            return returnMessage;
        }

        /// <summary>
        /// When overridden in a derived class, reads a message from a specified stream.
        /// </summary>
        /// <param name="stream">The <see cref="T:System.IO.Stream"/> object from which the message is read.</param>
        /// <param name="maxSizeOfHeaders">The maximum size of the headers that can be read from the message.</param>
        /// <param name="contentType">The Multipurpose Internet Mail Extensions (MIME) message-level content-type.</param>
        /// <returns>
        /// The <see cref="T:System.ServiceModel.Channels.Message"/> that is read from the stream specified.
        /// </returns>
        public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
        {
            Guard.ArgumentNotNull(stream, "stream");
            Stream compressStream = this.Compressor.CompressionStreamCreator(stream, CompressionMode.Decompress, false);
            return InnerEncoder.ReadMessage(compressStream, maxSizeOfHeaders);
        }

        /// <summary>
        /// When overridden in a derived class, writes a message to a specified stream.
        /// </summary>
        /// <param name="message">The <see cref="T:System.ServiceModel.Channels.Message"/> to write to the <paramref name="stream"/>.</param>
        /// <param name="stream">The <see cref="T:System.IO.Stream"/> object to which the <paramref name="message"/> is written.</param>
        public override void WriteMessage(Message message, Stream stream)
        {
            Guard.ArgumentNotNull(message, "message");
            using (Stream compressStream = this.Compressor.CompressionStreamCreator(stream, CompressionMode.Decompress,false))
            {
                this.InnerEncoder.WriteMessage(message, compressStream);
            }            
            stream.Flush();
        }

        /// <summary>
        /// When overridden in a derived class, writes a message of less than a specified size to a byte array buffer at the specified offset.
        /// </summary>
        /// <param name="message">The <see cref="T:System.ServiceModel.Channels.Message"/> to write to the message buffer.</param>
        /// <param name="maxMessageSize">The maximum message size that can be written.</param>
        /// <param name="bufferManager">The <see cref="T:System.ServiceModel.Channels.BufferManager"/> that manages the buffer to which the message is written.</param>
        /// <param name="messageOffset">The offset of the segment that begins from the start of the byte array that provides the buffer.</param>
        /// <returns>
        /// A <see cref="T:System.ArraySegment`1"/> of type byte that provides the buffer to which the message is serialized.
        /// </returns>
        public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
        {
            Guard.ArgumentNotNull(message, "message");
            Guard.ArgumentNotNull(bufferManager, "bufferManager");
            ArraySegment<byte> buffer = InnerEncoder.WriteMessage(message, maxMessageSize, bufferManager, 0);
            return this.Compressor.Compress(buffer, bufferManager, messageOffset);
        }
    }
}
